home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Freeware / Griffith 0.9.8 / griffith-0.9.8-win32.exe / {app} / lib / amazon.py < prev    next >
Text File  |  2008-11-17  |  17KB  |  393 lines

  1. __all__ = []
  2. """Python wrapper
  3.  
  4.  
  5. for Amazon web APIs
  6.  
  7. This module allows you to access Amazon's web APIs,
  8. to do things like search Amazon and get the results programmatically.
  9. Described here:
  10.   http://www.amazon.com/webservices
  11.  
  12. You need a Amazon-provided license key to use these services.
  13. Follow the link above to get one.  These functions will look in
  14. several places (in this order) for the license key:
  15. - the "license_key" argument of each function
  16. - the module-level LICENSE_KEY variable (call setLicense once to set it)
  17. - an environment variable called AMAZON_LICENSE_KEY
  18. - a file called ".amazonkey" in the current directory
  19. - a file called "amazonkey.txt" in the current directory
  20. - a file called ".amazonkey" in your home directory
  21. - a file called "amazonkey.txt" in your home directory
  22. - a file called ".amazonkey" in the same directory as amazon.py
  23. - a file called "amazonkey.txt" in the same directory as amazon.py
  24.  
  25. Sample usage:
  26. >>> import amazon
  27. >>> amazon.setLicense('...') # must get your own key!
  28. >>> pythonBooks = amazon.searchByKeyword('Python')
  29. >>> pythonBooks[0].ProductName
  30. u'Learning Python (Help for Programmers)'
  31. >>> pythonBooks[0].URL
  32. ...
  33. >>> pythonBooks[0].OurPrice
  34. ...
  35.  
  36. Other available functions:
  37. - browseBestSellers
  38. - searchByASIN
  39. - searchByUPC
  40. - searchByAuthor
  41. - searchByArtist
  42. - searchByActor
  43. - searchByDirector
  44. - searchByManufacturer
  45. - searchByListMania
  46. - searchSimilar
  47. - searchByWishlist
  48.  
  49. Other usage notes:
  50. - Most functions can take product_line as well, see source for possible values
  51. - All functions can take type="lite" to get less detail in results
  52. - All functions can take page=N to get second, third, fourth page of results
  53. - All functions can take license_key="XYZ", instead of setting it globally
  54. - All functions can take http_proxy="http://x/y/z" which overrides your system setting
  55. """
  56.  
  57. __author__ = "Mark Pilgrim (f8dy@diveintomark.org)"
  58. __version__ = "0.64.1"
  59. __cvsversion__ = "$Revision: 1.1 $"[11:-2]
  60. __date__ = "$Date: 2005/08/06 13:29:10 $"[7:-2]
  61. __copyright__ = "Copyright (c) 2002 Mark Pilgrim"
  62. __license__ = "Python"
  63. # Powersearch and return object type fix by Joseph Reagle <geek@goatee.net>
  64.  
  65. # Locale support by Michael Josephson <mike@josephson.org>
  66.  
  67. # Modification to _contentsOf to strip trailing whitespace when loading Amazon key
  68. # from a file submitted by Patrick Phalen.
  69.  
  70. # Support for specifying locale and associates ID as search parameters and 
  71. # internationalisation fix for the SalesRank integer conversion by
  72. # Christian Theune <ct@gocept.com>, gocept gmbh & co. kg
  73.  
  74. # Support for BlendedSearch contributed by Alex Choo
  75.  
  76. from xml.dom import minidom
  77. import os, sys, getopt, cgi, urllib, string
  78. try:
  79.     import timeoutsocket # http://www.timo-tasi.org/python/timeoutsocket.py
  80.     timeoutsocket.setDefaultSocketTimeout(10)
  81. except ImportError:
  82.     pass
  83.  
  84. LICENSE_KEY = ""
  85. ASSOCIATE = "webservices-20"
  86. HTTP_PROXY = None
  87. LOCALE = "us"
  88. # default API version is from 2005-10-05
  89. APIVERSION='2008-06-26'
  90.  
  91. # don't touch the rest of these constants
  92. class AmazonError(Exception): pass
  93. class NoLicenseKey(Exception): pass
  94. _amazonfile1 = ".amazonkey"
  95. _amazonfile2 = "amazonkey.txt"
  96. _licenseLocations = (
  97.     (lambda key: key, 'passed to the function in license_key variable'),
  98.     (lambda key: LICENSE_KEY, 'module-level LICENSE_KEY variable (call setLicense to set it)'),
  99.     (lambda key: os.environ.get('AMAZON_LICENSE_KEY', None), 'an environment variable called AMAZON_LICENSE_KEY'),
  100.     (lambda key: _contentsOf(os.getcwd(), _amazonfile1), '%s in the current directory' % _amazonfile1),
  101.     (lambda key: _contentsOf(os.getcwd(), _amazonfile2), '%s in the current directory' % _amazonfile2),
  102.     (lambda key: _contentsOf(os.environ.get('HOME', ''), _amazonfile1), '%s in your home directory' % _amazonfile1),
  103.     (lambda key: _contentsOf(os.environ.get('HOME', ''), _amazonfile2), '%s in your home directory' % _amazonfile2),
  104.     (lambda key: _contentsOf(_getScriptDir(), _amazonfile1), '%s in the amazon.py directory' % _amazonfile1),
  105.     (lambda key: _contentsOf(_getScriptDir(), _amazonfile2), '%s in the amazon.py directory' % _amazonfile2)
  106.     )
  107. _supportedLocales = {
  108.         "us" : (None, "ecs.amazonaws.com/onca/xml?Service=AWSECommerceService"),   
  109.         "uk" : ("uk", "ecs.amazonaws.co.uk/onca/xml?Service=AWSECommerceService"),
  110.         "de" : ("de", "ecs.amazonaws.de/onca/xml?Service=AWSECommerceService"),
  111.         "ca" : ("ca", "ecs.amazonaws.ca/onca/xml?Service=AWSECommerceService"),
  112.         "fr" : ("fr", "ecs.amazonaws.fr/onca/xml?Service=AWSECommerceService"),
  113.         "jp" : ("jp", "ecs.amazonaws.jp/onca/xml?Service=AWSECommerceService")
  114.     }
  115.  
  116. ## administrative functions
  117. def version():
  118.     print """PyAmazon %(__version__)s
  119. %(__copyright__)s
  120. released %(__date__)s
  121. """ % globals()
  122.  
  123. def setAssociate(associate):
  124.     global ASSOCIATE
  125.     ASSOCIATE=associate
  126.  
  127. def getAssociate(override=None):
  128.     return override or ASSOCIATE
  129.  
  130. ## utility functions
  131.  
  132. def _checkLocaleSupported(locale):
  133.     if not _supportedLocales.has_key(locale):
  134.         raise AmazonError, ("Unsupported locale. Locale must be one of: %s" %
  135.             string.join(_supportedLocales, ", "))
  136.  
  137. def setLocale(locale):
  138.     """set locale"""
  139.     global LOCALE
  140.     _checkLocaleSupported(locale)
  141.     LOCALE = locale
  142.  
  143. def getLocale(locale=None):
  144.     """get locale"""
  145.     return locale or LOCALE
  146.  
  147. def setLicense(license_key):
  148.     """set license key"""
  149.     global LICENSE_KEY
  150.     LICENSE_KEY = license_key
  151.  
  152. def getLicense(license_key = None):
  153.     """get license key
  154.  
  155.     license key can come from any number of locations;
  156.     see module docs for search order"""
  157.     for get, location in _licenseLocations:
  158.         rc = get(license_key)
  159.         if rc: return rc
  160.     raise NoLicenseKey, 'get a license key at http://www.amazon.com/webservices'
  161.  
  162. def setProxy(http_proxy):
  163.     """set HTTP proxy"""
  164.     global HTTP_PROXY
  165.     HTTP_PROXY = http_proxy
  166.  
  167. def getProxy(http_proxy = None):
  168.     """get HTTP proxy"""
  169.     return http_proxy or HTTP_PROXY
  170.  
  171. def getProxies(http_proxy = None):
  172.     http_proxy = getProxy(http_proxy)
  173.     if http_proxy:
  174.         proxies = {"http": http_proxy}
  175.     else:
  176.         proxies = None
  177.     return proxies
  178.  
  179. def _contentsOf(dirname, filename):
  180.     filename = os.path.join(dirname, filename)
  181.     if not os.path.exists(filename): return None
  182.     fsock = open(filename)
  183.     contents =  fsock.read().strip()
  184.     fsock.close()
  185.     return contents
  186.  
  187. def _getScriptDir():
  188.     if __name__ == '__main__':
  189.         return os.path.abspath(os.path.dirname(sys.argv[0]))
  190.     else:
  191.         return os.path.abspath(os.path.dirname(sys.modules[__name__].__file__))
  192.  
  193. class Bag: pass
  194.  
  195. def unmarshal(element):
  196.     rc = Bag()
  197.     if isinstance(element, minidom.Element) and (element.tagName == 'Details'):
  198.         rc.URL = element.attributes["url"].value
  199.     childElements = [e for e in element.childNodes if isinstance(e, minidom.Element)]
  200.     if childElements:
  201.         for child in childElements:
  202.             key = child.tagName
  203.             if hasattr(rc, key):
  204.                 if type(getattr(rc, key)) <> type([]):
  205.                     setattr(rc, key, [getattr(rc, key)])
  206.                 setattr(rc, key, getattr(rc, key) + [unmarshal(child)])
  207.             elif isinstance(child, minidom.Element) and (child.tagName == 'Details'):
  208.                 # make the first Details element a key
  209.                 setattr(rc,key,[unmarshal(child)])
  210.                 #dbg: because otherwise 'hasattr' only tests
  211.                 #dbg: on the second occurence: if there's a
  212.                 #dbg: single return to a query, it's not a
  213.                 #dbg: list. This module should always
  214.                 #dbg: return a list of Details objects.
  215.             else:
  216.                 setattr(rc, key, unmarshal(child))
  217.     else:
  218.         rc = "".join([e.data for e in element.childNodes if isinstance(e, minidom.Text)])
  219.         if element.tagName == 'SalesRank':
  220.             rc = rc.replace('.', '')
  221.             rc = rc.replace(',', '')
  222.             rc = int(rc)
  223.     return rc
  224.  
  225. def buildURL(search_type, searchfield, searchvalue, product_line, type, page, license_key, locale, associate):
  226.     _checkLocaleSupported(locale)
  227.     url = "http://" + _supportedLocales[locale][1]
  228.     if search_type == 'ItemLookup':
  229.         url += "&AssociateTag=%s" % associate
  230.         url += "&AWSAccessKeyId=%s" % license_key.strip()
  231.         url += "&ResponseGroup=%s" % type
  232.         if product_line:
  233.             url += "&SearchIndex=%s" % product_line
  234.         url += "&Operation=%s" % search_type
  235.         url += "&IdType=%s" % searchfield
  236.         url += "&ItemId=%s" % searchvalue
  237.     else:
  238.         url += "&AssociateTag=%s" % associate
  239.         url += "&AWSAccessKeyId=%s" % license_key.strip()
  240.         url += "&ResponseGroup=%s" % type
  241.         if page:
  242.             url += "&ItemPage=%s" % page
  243.         if product_line:
  244.             url += "&SearchIndex=%s" % product_line
  245.         url += "&Operation=%s" % search_type
  246.         url += "&%s=%s" % (searchfield, urllib.quote(searchvalue))
  247.         url += "&Sort=titlerank"
  248.     if not APIVERSION is None:
  249.         url += "&Version=%s" % APIVERSION
  250.     return url
  251.  
  252.  
  253. ## main functions
  254.  
  255.  
  256. def search(search_type, searchfield, searchvalue, product_line, type = "Large", page = None,
  257.            license_key=None, http_proxy = None, locale = None, associate = None):
  258.     """search Amazon
  259.  
  260.     You need a license key to call this function; see
  261.     http://www.amazon.com/webservices
  262.     to get one.  Then you can either pass it to
  263.     this function every time, or set it globally; see the module docs for details.
  264.  
  265.     Parameters:
  266.     keyword - keyword to search
  267.     search_type - in (KeywordSearch, BrowseNodeSearch, AsinSearch, UpcSearch, AuthorSearch, ArtistSearch, ActorSearch, DirectorSearch, ManufacturerSearch, ListManiaSearch, SimilaritySearch)
  268.     product_line - type of product to search for.  restrictions based on search_type
  269.         UpcSearch - in (music, classical)
  270.         AuthorSearch - must be "books"
  271.         ArtistSearch - in (music, classical)
  272.         ActorSearch - in (dvd, vhs, video)
  273.         DirectorSearch - in (dvd, vhs, video)
  274.         ManufacturerSearch - in (electronics, kitchen, videogames, software, photo, pc-hardware)
  275.     http_proxy (optional) - address of HTTP proxy to use for sending and receiving SOAP messages
  276.  
  277.     Returns: list of Bags, each Bag may contain the following attributes:
  278.       Asin - Amazon ID ("ASIN" number) of this item
  279.       Authors - list of authors
  280.       Availability - "available", etc.
  281.       BrowseList - list of related categories
  282.       Catalog - catalog type ("Book", etc)
  283.       CollectiblePrice - ?, format "$34.95"
  284.       ImageUrlLarge - URL of large image of this item
  285.       ImageUrlMedium - URL of medium image of this item
  286.       ImageUrlSmall - URL of small image of this item
  287.       Isbn - ISBN number
  288.       ListPrice - list price, format "$34.95"
  289.       Lists - list of ListMania lists that include this item
  290.       Manufacturer - manufacturer
  291.       Media - media ("Paperback", "Audio CD", etc)
  292.       NumMedia - number of different media types in which this item is available
  293.       OurPrice - Amazon price, format "$24.47"
  294.       ProductName - name of this item
  295.       ReleaseDate - release date, format "09 April, 1999"
  296.       Reviews - reviews (AvgCustomerRating, plus list of CustomerReview with Rating, Summary, Content)
  297.       SalesRank - sales rank (integer)
  298.       SimilarProducts - list of Product, which is ASIN number
  299.       ThirdPartyNewPrice - ?, format "$34.95"
  300.       URL - URL of this item
  301.     """
  302.     license_key = getLicense(license_key)
  303.     locale = getLocale(locale)
  304.     associate = getAssociate(associate)
  305.     url = buildURL(search_type, searchfield, searchvalue, product_line, type, page, 
  306.             license_key, locale, associate)
  307.     proxies = getProxies(http_proxy)
  308.     u = urllib.FancyURLopener(proxies)
  309.     usock = u.open(url)
  310.     xmldoc = minidom.parse(usock)
  311.  
  312. #     from xml.dom.ext import PrettyPrint
  313. #     PrettyPrint(xmldoc)
  314.  
  315.     usock.close()
  316.     data = unmarshal(xmldoc)
  317.     if search_type == "BlendedSearch":
  318.         if hasattr(data, 'BlendedSearch'):
  319.             data = data.BlendedSearch
  320.     elif hasattr(data, 'ItemSearchResponse'):
  321.         data = data.ItemSearchResponse 
  322.     elif hasattr(data, 'ItemLookupResponse'):
  323.         data = data.ItemLookupResponse 
  324.  
  325.     if hasattr(data, 'Errors'):
  326.         raise AmazonError, data.Errors
  327.     else:
  328.         if search_type == "BlendedSearch":
  329.             return data 
  330.         else:            
  331.             if hasattr(data, 'Items'):
  332.                 return data.Items
  333.             else:
  334.                 return data
  335.  
  336. def searchByKeyword(keyword, product_line="Books", type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  337.     return search("ItemSearch", 'Keywords', keyword, product_line, type, page, license_key, http_proxy, locale, associate)
  338.  
  339. def searchByTitle(keyword, product_line="Books", type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  340.     return search("ItemSearch", 'Title', keyword, product_line, type, page, license_key, http_proxy, locale, associate)
  341.  
  342. def browseBestSellers(browse_node, product_line="Books", type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  343.     return search("BrowseNodeSearch", 'Keywords', browse_node, product_line, type, page, license_key, http_proxy, locale, associate)
  344.  
  345. def searchByASIN(ASIN, type="Large", license_key=None, http_proxy=None, locale=None, associate=None):
  346.     return search("ItemLookup", 'ASIN', ASIN, None, type, None, license_key, http_proxy, locale, associate)
  347.  
  348. def searchByUPC(UPC, product_line="Books", type="Large", license_key=None, http_proxy=None, locale=None, associate=None):
  349.     return search("ItemLookup", 'UPC', UPC, product_line, type, None, license_key, http_proxy, locale, associate)
  350.  
  351. def searchByEAN(EAN, product_line="Books", type="Large", license_key=None, http_proxy=None, locale=None, associate=None):
  352.     return search("ItemLookup", 'EAN', EAN, product_line, type, None, license_key, http_proxy, locale, associate)
  353.  
  354. def searchByAuthor(author, type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  355.     return search("AuthorSearch", 'Keywords', author, "Books", type, page, license_key, http_proxy, locale, associate)
  356.  
  357. def searchByArtist(artist, product_line="Music", type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  358.     if product_line not in ("music", "classical"):
  359.         raise AmazonError, "product_line must be in ('Music', 'Classical')"
  360.     return search("ArtistSearch", 'Keywords', artist, product_line, type, page, license_key, http_proxy, locale, associate)
  361.  
  362. def searchByActor(actor, product_line="DVD", type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  363.     if product_line not in ("DVD", "VHS", "Video"):
  364.         raise AmazonError, "product_line must be in ('DVD', 'VHS', 'Video')"
  365.     return search("ActorSearch", 'Keywords', actor, product_line, type, page, license_key, http_proxy, locale, associate)
  366.  
  367. def searchByDirector(director, product_line="DVD", type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  368.     if product_line not in ("DVD", "VHS", "Video"):
  369.         raise AmazonError, "product_line must be in ('DVD', 'VHS', 'Video')"
  370.     return search("DirectorSearch", 'Keywords', director, product_line, type, page, license_key, http_proxy, locale, associate)
  371.  
  372. def searchByManufacturer(manufacturer, product_line="pc-hardware", type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  373.     if product_line not in ("electronics", "kitchen", "videogames", "software", "photo", "pc-hardware"):
  374.         raise AmazonError, "product_line must be in ('electronics', 'kitchen', 'videogames', 'software', 'photo', 'pc-hardware')"
  375.     return search("ManufacturerSearch", 'Keywords', manufacturer, product_line, type, page, license_key, http_proxy, locale, associate)
  376.  
  377. def searchByListMania(listManiaID, type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  378.     return search("ListManiaSearch", 'Keywords', listManiaID, None, type, page, license_key, http_proxy, locale, associate)
  379.  
  380. def searchSimilar(ASIN, type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  381.     return search("SimilaritySearch", 'Keywords', ASIN, None, type, page, license_key, http_proxy, locale, associate)
  382.  
  383. def searchByWishlist(wishlistID, type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  384.     return search("WishlistSearch", 'Keywords', wishlistID, None, type, page, license_key, http_proxy, locale, associate)
  385.  
  386. def searchByPower(keyword, product_line="Books", type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  387.     return search("PowerSearch", 'Keywords', keyword, product_line, type, page, license_key, http_proxy, locale, associate)
  388.     # >>> RecentKing = amazon.searchByPower('author:Stephen King and pubdate:2003')
  389.     # >>> SnowCrash = amazon.searchByPower('title:Snow Crash')
  390.  
  391. def searchByBlended(keyword, type="Large", page=1, license_key=None, http_proxy=None, locale=None, associate=None):
  392.     return search("BlendedSearch", 'Keywords', keyword, None, type, page, license_key, http_proxy, locale, associate)
  393.